package cn.org.itool.ws.framework.utils;

import cn.org.itool.common.constent.ErrorCode;
import cn.org.itool.common.constent.WsCodeEnum;
import cn.org.itool.ws.framework.constent.FtMessageTypeEnum;
import cn.org.itool.ws.framework.netty.auth.Principal;
import cn.org.itool.ws.framework.netty.message.BizWsMessage;
import cn.org.itool.ws.framework.protocol.pomelo.Protocol;
import cn.org.itool.ws.pojo.BizMessage;
import cn.org.itool.ws.pojo.UserTag;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * {@link Channel}工具类
 *
 */
public class ChannelUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(ChannelUtils.class);
    private static final AttributeKey<Principal> PRINCIPAL_ATT_KEY =  AttributeKey.newInstance("principal_key");

    private static final Set<String> DEFAULT_TAG = new HashSet<>();

    static {
        DEFAULT_TAG.add("default");
    }

    private static final String USER_TAG_KEY = "userTags";

    private ChannelUtils(){
        //none instance
    }

    /**
     * 设置认证用户到channel中
     *
     * @param channel {@link Channel}
     * @param principal {@link Principal}
     * @return true 设置成功
     */
    public static boolean setPrincipal(Channel channel, Principal principal){
        Attribute<Principal> attr = channel.attr(PRINCIPAL_ATT_KEY);
        return attr.compareAndSet(null, principal);
    }

    public static UserTag getUserTag(Channel channel) {
        UserTag userTag = new UserTag();
        Attribute<Principal> attr = channel.attr(PRINCIPAL_ATT_KEY);
        Principal principal = attr.get();
        Optional<Set<String>> optionalStrings = principal.getAttr(USER_TAG_KEY);
        userTag.setUserId(principal.getId());
        userTag.setTags(optionalStrings.orElse(DEFAULT_TAG));
        return userTag;
    }

    public static String getUserId(Channel channel) {
        Attribute<Principal> attr = channel.attr(PRINCIPAL_ATT_KEY);
        Principal principal = attr.get();
        return principal.getId();
    }

    public static Object setUserTag(Channel channel, Set<String> tags){
        Attribute<Principal> attr = channel.attr(PRINCIPAL_ATT_KEY);
        return attr.get().setAttr(USER_TAG_KEY, tags);
    }

    /**
     * 判断channel 是否 被包含
     *
     * @param channel {@link Channel}
     * @param userId 用户id
     * @return true 设置成功
     */
    public static boolean containsUser(Channel channel, List<String> userId){
        Attribute<Principal> attr = channel.attr(PRINCIPAL_ATT_KEY);
        return userId.contains(attr.get().getId());
    }

    /**
     * 获取用户授权
     *
     * @param channel {@link Channel}
     * @return {@link Principal}
     */
    public static Principal getPrincipal(Channel channel){
        Attribute<Principal> attr = channel.attr(PRINCIPAL_ATT_KEY);
        Principal principal = attr.get();
        if(principal == null){
            throw new IllegalStateException("Principal not exist");
        }
        return principal;
    }

    /**
     * 获取授权用户， 可以不存在
     *
     * @param channel {@link Channel}
     * @return {@link Principal}
     */
    public static Optional<Principal> getPrincipalOpt(Channel channel){
        Attribute<Principal> attr = channel.attr(PRINCIPAL_ATT_KEY);
        return Optional.ofNullable(attr.get());
    }


    /**
     * 发送消息并关闭链接
     *
     * @param channel {@link Channel}
     * @param msg {@link Protocol.Message}
     */
    public static void writeAndClose(Channel channel, BizWsMessage msg){
        ChannelFuture future = channel.writeAndFlush(msg);
        if(LOGGER.isDebugEnabled()){
            LOGGER.debug("Write close channelId={}", channel.id().asLongText());
        }
        future.addListener(f -> {
            if(f.isDone() && channel.isActive()){
                LOGGER.debug("Write close success channelId={}", channel.id().asLongText());
                channel.close();
            }
        });
    }

    /**
     * 获取客户端IP地址
     *
     * @param channel {@link Channel}
     * @return ctx
     */
    public static String getClientIp(Channel channel){
        InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress();
        return remoteAddress.getAddress().getHostAddress();
    }

    /**
     * 踢用户下线
     *
     * @param channel {@link Channel}
     */
    public static void forceOffline(Channel channel){
        close(channel, WsCodeEnum.OTHER_LOGIN_OFFLINE);
    }

    /**
     * 发送消息并关闭链接
     * @param channel {@link Channel}
     * @param errorResp {@link ErrorCode}
     */
    public static void close(Channel channel, ErrorCode errorResp){
        BizWsMessage websocketMessage = new BizWsMessage();
        websocketMessage.setRoute("onClose");
        websocketMessage.setType(FtMessageTypeEnum.SERVER_PUSH.getCode());

        BizMessage<String> bizMessage = new BizMessage<>();
        bizMessage.setCode(errorResp.code());
        bizMessage.setMsg(errorResp.msg());
        websocketMessage.setBody(bizMessage);
        ChannelUtils.writeAndClose(channel, websocketMessage);
    }
}
